/**
 * @author ens12ilt - ens12ple
 */

package model;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

import config.Config;

import log.Log;
import utils.VectorUtils;

public final class Image implements Cloneable{


	private static final String LOG_TAG = Image.class.getSimpleName();

	private static final int ROW_SIZE = 20;
	private static final int COL_SIZE = 20;
	private static final int MAX_GREY_LEVEL = 31;

	private int[][] _image;
	private int _type;
	private String _name;

	/**
	 * 
	 * @param name image name
	 */
	public Image(String name){
		_name = name;
		_image = new int[ROW_SIZE][COL_SIZE];
		_type = -1;
	}

	public void setImage(int[][] i){
		_image = i;
	}

	public Image(Image i){
		int[][] toSet = new int[i._image.length][i._image[0].length];
		for(int k = 0; k < toSet.length; k++){
			toSet[k] = Arrays.copyOf(i._image[k], i._image[k].length);
		}
		_image = toSet;
		_name = i._name;
		_type = i._type;
	}

	public int getType(){
		return _type;
	}

	/**
	 * 
	 * @param p position of current dark pixel
	 * @param pos array of positions of all dark pixels
	 * @param aux array of neighbors of p
	 */
	private void subSetBlackZone(Position p, ArrayList<Position> pos, ArrayList<Position> aux){
		ArrayList<Position> queue = new ArrayList<Position>(pos);
		if(pos.size() == 0){
			return;
		}else{
			queue.remove(0);
			if(pos.get(0).getX() == p.getX() && pos.get(0).getY() == p.getY() + 1 
					||
					pos.get(0).getX() == p.getX() && pos.get(0).getY() == p.getY() - 1 
					||
					pos.get(0).getX() == p.getX() + 1 && pos.get(0).getY() == p.getY()
					||
					pos.get(0).getX() == p.getX() - 1 && pos.get(0).getY() == p.getY()
					||
					pos.get(0).getX() == p.getX() + 1 && pos.get(0).getY() == p.getY() + 1
					||
					pos.get(0).getX() == p.getX() - 1 && pos.get(0).getY() == p.getY() - 1
					||
					pos.get(0).getX() == p.getX() + 1 && pos.get(0).getY() == p.getY() - 1
					||
					pos.get(0).getX() == p.getX() - 1 && pos.get(0).getY() == p.getY() + 1
					){
				aux.add(pos.get(0));
				subSetBlackZone(pos.get(0), queue, aux);
			}else{
				subSetBlackZone(p, queue, aux);
			}
		}
	}

	/**
	 * 
	 * @return the positions of darkest pixels
	 */
	public ArrayList<Subset> getPositionsForMaxValues(){
		ArrayList<Position> pos = new ArrayList<Position>();
		for(int i = 0; i < _image.length; i++){
			for(int j = 0; j < _image[i].length; j++){
				if(_image[i][j] == MAX_GREY_LEVEL){
					pos.add(new Position(j, i));
				}
			}
		}
		ArrayList<Subset> subsets = new ArrayList<Subset>();
		while(pos.size() > 0){
			Position p = pos.get(0);
			pos.remove(0);
			ArrayList<Position> subset = new ArrayList<Position>();
			subSetBlackZone(p, pos, subset);

			for(Position ps : subset){
				pos.remove(ps);
			}
			subsets.add(new Subset(subset));
		}
		return subsets;
	}

	/**
	 * 
	 * @param subsets subset containing the possible eyes
	 * @return the two maximum subsets
	 */
	public int[] getLargerSubsets(ArrayList<Subset> subsets){
		int[] max = new int[2];
		int maxSize;
		maxSize = 0;
		for(int i = 0; i < subsets.size(); i++){
			Subset s = subsets.get(i);
			if(s.getSet().size() > maxSize){
				maxSize = s.getSet().size();
				max[0] = i;
			}
		}
		maxSize = 0;
		for(int i = 0; i < subsets.size(); i++){
			Subset s = subsets.get(i);
			if(s.getSet().size() > maxSize && i != max[0]){
				maxSize = s.getSet().size();
				max[1] = i;
			}
		}
		return max;
	}

	private int[][] getMiddleOfEyes(){
		ArrayList<Subset> subsetsTmp = getPositionsForMaxValues();
		int[] electedSubsets = getLargerSubsets(subsetsTmp);
		ArrayList<Subset> subsets = new ArrayList<Subset>();
		subsets.add(subsetsTmp.get(electedSubsets[0]));
		subsets.add(subsetsTmp.get(electedSubsets[1]));

		int[][] ret = new int[2][2];
		for(int j = 0; j < subsets.size(); j++){

			Subset s = subsets.get(j);
			Log.i(LOG_TAG, s + "");
			if(s.getSet().size() > 0){
				int maxpos, minpos;
				double max = VectorUtils.norm2(s.getSet().get(0).toVect());
				double min = VectorUtils.norm2(s.getSet().get(0).toVect());
				maxpos = minpos = 0;
				for(int i = 1; i < s.getSet().size(); i++){
					if(VectorUtils.norm2(s.getSet().get(i).toVect()) > max){
						max = VectorUtils.norm2(s.getSet().get(0).toVect());
						maxpos = i;
					}
					if(VectorUtils.norm2(s.getSet().get(i).toVect()) < min){
						min = VectorUtils.norm2(s.getSet().get(0).toVect());
						minpos = i;
					}
				}
				ret[j][0] = (s.getSet().get(minpos).getX() + s.getSet().get(maxpos).getX())/2;
				ret[j][1] = (s.getSet().get(minpos).getY() + s.getSet().get(maxpos).getY())/2;
			}
		}
		return ret;
	}

	/**
	 * 
	 * @return a vector between middle of eyes
	 */
	public int[] createVectorFromMiddleOfEyes(){
		int[][] mid = getMiddleOfEyes();
		int[] ret = new int[2];
		int x = (mid[1][0] - mid[0][0]);
		int y = (mid[1][1] - mid[0][1]);
		ret[0] = -x;
		ret[1] = -y;
		return ret;
	}


	public void setImageRow(int i, int[] values){
		for(int j = 0; j < COL_SIZE; j++)
			_image[i][j] = values[j];
	}

	public void setType(int type){
		_type = type;
	}

	public int[][] getImage(){
		return _image;
	}

	/**
	 * 
	 * @return the image in an array of one dimension
	 */
	public ArrayList<Double> getImageLinear(){
		ArrayList<Double> ret = new ArrayList<Double>();
		for(int i = 0; i < _image.length; i++){
			for(int j = 0; j < _image[i].length; j++){
				ret.add(Double.valueOf(_image[i][j]));
			}
		}
		return ret;
	}

	public String getName(){
		return _name;
	}

	private String imageToString(){
		String str = "";
		for(int i = 0; i < _image.length; i++){
			for(int j = 0; j < _image[i].length; j++){
				str += 31 - _image[i][j] + " ";
			}
			str += "\n";
		}
		return str;
	}

	@Override
	public String toString() {
		String str = "IMAGE NAME : " + _name + " - TYPE : "+ _type +"\n";
		str += imageToString();
		return str;
	}

	/**
	 * 
	 * @return average threshold of the figure
	 */
	public int getAverageThreshold(){
		int sum = 0;
		double avg;
		for(int i = 0; i < _image.length; i++){
			for(int j = 0; j < _image[i].length; j++){
				sum += _image[i][j];
			}
		}
		avg = sum / (COL_SIZE * ROW_SIZE);
		return (int) avg;
	}

	/**
	 * 
	 * @param folderName the name of the existing folder where stores generated pgm.
	 */
	public void generatePGM(String folderName){
		if(Config.DEBUG){
			try {
				FileWriter fw = new FileWriter(folderName+"/"+_name+".pgm");
				String header = "P2\n" +
						_image[0].length + " " + _image.length +"\n" +
						MAX_GREY_LEVEL + "\n";
				fw.append(header);
				fw.append(imageToString());
				fw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
